let counter = 0, power = 0

const sign = Object.freeze({
    eol         : iota(), // keep eol
    // leader start
    '0'         : iota(),
    '1'         : iota(),
    '2'         : iota(),
    '3'         : iota(),
    '4'         : iota(),
    '5'         : iota(),
    '6'         : iota(),
    '7'         : iota(),
    '8'         : iota(),
    '9'         : iota(),

    '0b'        : iota(),
    '0f'        : iota(),
    '0x'        : iota(),

    '"'         : iota(),
    '\''        : iota(),
    '\\'        : iota(),
    '`'         : iota(),
    // leader end

    '('         : iota(),
    ')'         : iota(),
    '['         : iota(),
    ']'         : iota(),
    '{'         : iota(),
    '}'         : iota(),

    '!'         : iota(),
    '#'         : iota(),
    '$'         : iota(),
    ','         : iota(),
    '...'       : iota(),
    ':'         : iota(),
    '?'         : iota(),
    '@'         : iota(),
    '_'         : iota(),

    // declaration
    extends     : iota(4),
    struct      : iota(4),
    traits      : iota(4),

    // statement
    break       : iota(4, ++power),
    case        : iota(4),
    catch       : iota(4),
    continue    : iota(4),
    defer       : iota(4),
    echo        : iota(4),
    elif        : iota(4),
    else        : iota(4),
    fn          : iota(4),
    for         : iota(4),
    if          : iota(4),
    of          : iota(4),
    out         : iota(4),
    switch      : iota(4),
    throw       : iota(4),
    try         : iota(4),
    yield       : iota(4),

    // assignment
    '='         : iota(0, ++power),
    ':='        : iota(),
    '%='        : iota(),
    '&='        : iota(),
    '**='       : iota(),
    '*='        : iota(),
    '+='        : iota(),
    '-='        : iota(),
    '/='        : iota(),
    '<<='       : iota(),
    '>>='       : iota(),
    '>>>='      : iota(),
    '^='        : iota(),
    '|='        : iota(),
    '~='        : iota(),

    // operator
    '..'        : iota(0, ++power),
    or          : iota(4 | 3, ++power),
    and         : iota(4 | 3, ++power),

    un          : iota(4 | 1, ++power),

    '>'         : iota(3, ++power),
    '>='        : iota(3),
    '<'         : iota(3),
    '<='        : iota(3),
    '!='        : iota(3),
    '=='        : iota(3),
    in          : iota(4 | 3),
    is          : iota(4 | 3),
    not         : iota(4 | 3),
    notin       : iota(4 | 3),

    '|'         : iota(3, ++power),
    '^'         : iota(3, ++power),
    '&'         : iota(3, ++power),

    '<<'        : iota(3, ++power),
    '>>'        : iota(3),
    '>>>'       : iota(3),

    '+'         : iota(3, ++power),
    '-'         : iota(3),

    '%'         : iota(3, ++power),
    '*'         : iota(3),
    '/'         : iota(3),

    '**'        : iota(3, ++power),

    '+x'        : iota(1, ++power),
    '-x'        : iota(1),
    '~'         : iota(1),


    '++'        : iota(2, ++power),
    '--'        : iota(2),

    '.'         : iota(0, ++power),

    // value

    ident       : iota(),

    super       : iota(4),
    this        : iota(4),
    iota        : iota(4),

    Infinity    : iota(4),
    NaN         : iota(4),
    false       : iota(4),
    null        : iota(4),
    true        : iota(4),

  }),

  ASSIGNMENT = powerOf(sign['=']),

  T         = sign['`'],
  INF       = sign.Infinity,

  keys      = Object.freeze(entries())

function entries () {
  return Object.keys(sign).sort((a, b) => {
    return (sign[a] & 0xFF) < (sign[b] & 0xFF) && -1 || 1
  })
}

function keyOf (sign) {
  return keys[sign & 0xFF]
}

function isLiteral (sign) {
  return sign <= T || sign >= INF
}

function isKeyword (sign) {
  return (sign & 0x400) === 0x400
}

function isPrefix (sign) {
  return (sign & 0x300) === 0x100
}

function isInfix (sign) {
  return (sign & 0x300) === 0x300
}

function isSuffix (sign) {
  return (sign & 0x300) === 0x200
}

function isAssign (sign) {
  return powerOf(sign) === ASSIGNMENT
}

function powerOf (sign) {
  return sign & 0xFFFFF000
}

function indexOf (sign) {
  return sign & 0xFF
}

function iota (flag) {
  flag = flag | 0
  return power << 12 | flag << 8 | counter++
}

export {
  indexOf,
  isAssign,
  isLiteral,
  isInfix,
  isPrefix,
  isSuffix,
  isKeyword,
  keyOf,
  powerOf
}
export default sign
